package a02;

import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.rmi.RemoteException;
import java.util.List;
import java.util.Stack;

import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.Timer;

import rmi.IGameServer;

import util.Pair;
import util.SoundManager;

public class RegularControl<M> extends JPanel implements Constants {

    private static final long serialVersionUID = 1L;
    private RegularBoard<M> board;
    private JFrame frame;
    private RegularGame<M> game;

    private boolean gameMode1Player = false;

    private MainMenu<M> menu;
    private SaveGameManager<M> saveman;
    private Stack<RegularGame<M>> stack;
    private SoundManager sound;
    private boolean soundEnabled = true;
    private Timer timer;

    private IGameServer server;
    public byte playerID = 0;
    private boolean isClient = false;
    private boolean gameStarted = false;

    public void setSoundEnabled(boolean soundEnabled) {
	this.soundEnabled = soundEnabled;
    }

    public RegularControl(RegularGame<M> game) {
	this(game, new RegularBoard<M>(game), "Kein Fenstername", 0, true);
    }

    public RegularControl(RegularGame<M> game, RegularBoard<M> b, String title,
	    int kiLevel, boolean visible) {
	this.saveman = new SaveGameManager<M>(this);
	this.menu = new MainMenu<M>(this);
	this.stack = new Stack<RegularGame<M>>();
	this.frame = this.menu.getNewFrame(title);
	this.frame.add(this.menu.getPanel(kiLevel));
	this.setName(title);
	this.setGame(game);
	this.setBoard(b);
	addActionListeners();
	this.timer.start();
	this.add(this.getBoard());
	this.frame.setMaximumSize(new Dimension(600, 660));
	this.frame.setMinimumSize(new Dimension(350, 350));
	this.frame.pack();
	this.frame.setLocationRelativeTo(null);
	this.frame.setResizable(false);
	refresh();
	this.frame.setVisible(visible);	
    }

    private void addActionListeners() {
	this.getBoard().addMouseListener(new MouseAdapter() {
	    @Override
	    public void mousePressed(MouseEvent ev) {
		byte c = (byte) (ev.getPoint().getX() / RegularControl.this
			.getBoard().getUNIT());
		byte r = (byte) (ev.getPoint().getY() / RegularControl.this
			.getBoard().getUNIT());
		try {
		    if (clickAllowed())
			RegularControl.this.whenMousePressed(c, r);
		} catch (RemoteException e) {
		    showPopup("Verbindungsfehler",
			    "Server nicht mehr erreichbar!");
		    backToMenu();
		}

	    }
	});
	this.timer = new Timer(30, new ActionListener() {
	    public void actionPerformed(ActionEvent e) {
		try {
		    if (!(board == null)) doOnTick();
		} catch (RemoteException e1) {
		    showPopup("Verbindungsfehler",
			    "Server nicht mehr erreichbar!");
		    backToMenu();
		}
	    }
	});
    }

    public boolean clickAllowed() throws RemoteException {
	boolean result = false;
	result = (server == null) || (server != null && (server.hasClient()));
	return result;
    }

    public void doOnTick() throws RemoteException {
	if (getServer() != null && getServer().isAlive())getMenu().getChat().checkForNews();
	
	if (!(getBoard().getGame().ended())
		&& (getServer() != null && getServer().isAlive())) {
	    if (clickAllowed()) {
		refresh();
		if (!gameStarted ) {
		    showPopup("Verbunden!", "Verbunden mit "+getServer().getOpponentName(getPlayerID()));
		    gameStarted = true;
		}
		RegularControl.this.whenMousePressed((byte) -1, (byte) -1);
	    } else {
		menu.setStatus("WARTE AUF CLIENT, IP " + getServer().getIP(),
			true);
	    }
	} else if ((getServer() != null) && !getServer().isAlive()) {	    
	    JOptionPane.showMessageDialog(this, "Ihr Mitspieler hat die Partie verlassen! ",
		    "Server Shutdown", 1);
	    this.backToMenu();
	} else {
	    menu.setButtonEnabled(false);
	}
    }

    public RegularControl(RegularGame<M> game, String title) {
	this(game, new RegularBoard<M>(game), title, 0, true);
    }

    public RegularControl(RegularGame<M> game, String title, boolean visible) {
	this(game, new RegularBoard<M>(game), title, 0, visible);
    }

    public RegularControl(RegularGame<M> game, String title, int kiLevel,
	    boolean visible) {
	this(game, new RegularBoard<M>(game), title, kiLevel, visible);
    }

    public RegularControl(RegularGame<M> game, String title, int kiLevel) {
	this(game, new RegularBoard<M>(game), title, kiLevel, true);
    }

    public void clearStack() {
	this.stack.clear();
	this.menu.setButtonEnabled(false);
    }

    public void close() {
	this.frame.dispose();
    }

    public RegularBoard<M> getBoard() {
	return this.board;
    }

    public RegularGame<M> getGame() {
	return this.game;
    }

    public MainMenu<M> getMenu() {
	return this.menu;
    }

    public void setServer(IGameServer stub) {
	this.server = stub;
	getMenu().setLoadSaveEnabled(false);
    }

    public IGameServer getServer() {
	return this.server;
    }
    
    public JFrame getFrame() {
	return this.frame;
    }

    public RegularGame<M> getNewGame() {
	System.out.println("reset aus regularcontrol");
	return null;
    }

    public SaveGameManager<M> getSaveGameManager() {
	return this.saveman;
    }

    public Stack<RegularGame<M>> getStack() {
	return this.stack;
    }

    public boolean isGameMode1Player() {
	return this.gameMode1Player;
    }

    public void refresh() {
	try {
	    if ((getServer() != null && getServer().getPlayerID() == this
		    .getPlayerID())
		    || (getServer() == null && this.getBoard().getGame()
			    .currentPlayer() == this.getPlayerID())) {
		menu.setStatus("Sie sind am Zug! ("
			+ this.getBoard()
				.getPlayerColorName(this.getPlayerID()) + ")",
			false);
	    } else if ((getServer() != null && getServer().getPlayerID() != this
		    .getPlayerID())
		    || (getServer() == null && this
			    .getBoard()
			    .getGame()
			    .otherPlayer(
				    this.getBoard().getGame().currentPlayer()) == this
			    .getPlayerID())) {
		menu.setStatus("Warten auf Zug des Mitspielers..", true);
	    } else if (getServer() == null && !isGameMode1Player()) {
		menu.setStatus("Spieler "
			+ this.getBoard().getPlayerColorName(
				this.getBoard().getGame().currentPlayer())
			+ " ist an der Reihe ");
	    }
	} catch (RemoteException e) {
	    showPopup("Verbindungsfehler", "Server nicht mehr erreichbar!");
	    backToMenu();
	}
    }

    public void reset() {
	RegularGame<M> game = this.getNewGame();
	if (!(game == null)) {
	    this.getBoard().setGame(game);
	}
	this.clearStack();
	refresh();
    }

    public void setBoard(RegularBoard<M> board) {
	this.board = board;
    }

    private void setGame(RegularGame<M> game) {
	this.game = game;
    }

    public void setGameMode1Player(boolean gameMode1Player) {
	this.gameMode1Player = gameMode1Player;
    }

    public void setStatus(String status) {
	this.menu.setStatus(status);
    }

    public void showPopup(String title, String message) {
	JOptionPane.showMessageDialog(this, message, title, 1);
    }

    public void undo() {
	this.getBoard().setGame(this.stack.pop());
	if (this.stack.isEmpty()) {
	    this.menu.setButtonEnabled(false);
	}
	refresh();
    }

    public void whenMousePressed(byte c, byte r) throws RemoteException {
	boolean remoteMove = false;
	if (this.getBoard().getGame().ended()) {
	    playSound(Sound.FAIL);
	    return;
	}

	if (!(getServer() == null)
		&& (c == -1)
		&& !(getServer().versionEquals(getServer().getVersion(
			this.getPlayerID())))) {
	    c = getServer().getPosX();
	    r = getServer().getPosY();
	    getServer().setVersion(getPlayerID(), getServer().getVersion());
	    remoteMove = true;
	} else if (!(getServer() == null) && (c > -1)
		&& !(getServer().getPlayerID() == getPlayerID())) {
	    c = -1;
	    r = -1;
	}

	if (c > -1)
	    executeChanges(c, r, remoteMove);

    }

    public void executeChanges(byte c, byte r, boolean remoteMove)
	    throws RemoteException {
	if (this.getBoard().getGame().getAtPosition(c, r) != this.getBoard()
		.getGame().getPlayerNone()
		|| (this.getServer() != null && this.getServer().getPlayerID() != this
			.getPlayerID())) {
	    if (this.getBoard().getGame().getAtPosition(c, r) != this
		    .getBoard().getGame().getPlayerNone())
		playSound(Sound.FAIL);
	    return;
	}

	RegularGame<M> newState = this.getBoard().getGame().setAtPosition(c, r);
	if (newState.getAtPosition(c, r) == this.getBoard().getGame()
		.getPlayerNone()) {
	    playSound(Sound.FAIL);

	} else {
	    playSound(Sound.CLICK);
	    this.stack.push(this.getBoard().getGame());
	    this.getBoard().setGame(newState);
	    if (!(getServer() == null) && !remoteMove)
		this.getServer().setMouseClick(c, r, getPlayerID());
	    this.getBoard().setLastMove(new Pair<Byte, Byte>(c, r));
	}
	refresh();
	handleWin();
	if (!this.stack.isEmpty()) {
	    this.menu.setButtonEnabled(true);
	}
    }

    public byte getPlayerID() {
	return playerID;
    }

    public void setPlayerID(byte id) {
	this.playerID = id;
    }

    public void handleWin() {
	if (this.getBoard().getGame().ended()) {
	    this.menu.setStatus("Spiel beendet! ");
	    playSound(Sound.WIN);	    
		if (getServer() == null && isCurrentPlayerWinner()) {
		    this.showPopup(
			    "GEWONNEN",
			    "Spieler "
				    + this.getBoard()
					    .getPlayerColorName(
						    this.getBoard()
							    .getGame()
							    .otherPlayer(
								    this.getBoard()
									    .getGame()
									    .currentPlayer()))
				    + " hat gewonnen!");
		} else if (getPlayerID() != 0
			&& (getBoard().getGame().wins(this.getPlayerID()))) {
		    this.showPopup("Gewonnen", "Sie haben gewonnen!");
		
		} else if (getPlayerID() != 0
			&& (getBoard().getGame().wins(this.getBoard().getGame().otherPlayer(this.getPlayerID())))) {
		    this.showPopup("Verloren", "Sie haben verloren!");
		} else {
		    this.showPopup("Unentschieden", "Leider unentschieden!");
		}
	}
    }

    public boolean isCurrentPlayerWinner() {
	return this
		.getBoard()
		.getGame()
		.wins(this.getBoard().getGame()
			.otherPlayer(this.getBoard().getGame().currentPlayer()));
    }

    public void playSound(String fileName) {
	if (isSoundEnabled()) {
	    sound = new SoundManager(fileName);
	    sound.play();
	}
    }

    public boolean isSoundEnabled() {
	return this.soundEnabled;
    }

    public boolean isPassingEnabled() {
	return false;
    }

    public void pass() {
    }

    public List<Pair<Byte, Byte>> getWinningLine() {
	return null;
    }

    public void backToMenu() {
	this.timer=null;
	this.board=null;
	StartMenu menu = new StartMenu(isSoundEnabled());
	if (isGameMode1Player())
	    menu.setRadioButton2enabled(true);
	try {
	    if ((server != null && !isClient())) {
		serverDisconnect();
	    } else if ((server != null && isClient())) {
		getServer().clientDisconnect();
	    }

	} catch (RemoteException e) {
	    server = null;	   
	}
	this.close();
    }

    public boolean isClient() {
	return this.isClient;
    }

    public void makeClient() {
	this.isClient = true;
    }

    public void serverDisconnect() throws RemoteException {
	if ((getServer() != null) && getServer().getHostID() == getPlayerID()) {
	    getServer().shutdown();
	    setServer(null);
	}
    }

    @Override
    public void setVisible(boolean visible) {
	this.frame.setVisible(visible);
    }

}
